/*
 * Copyright 2011-2012 Redia A/S
 * 
 * This file is part of Indhold+ for Android.
 *
 *  Redia Library App for Android is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Redia Library App for Android is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with Redia Library App for Android.  If not, see <http://www.gnu.org/licenses/>.
 */
package dk.redia.digiform.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.bluetooth.BluetoothSocket;
import android.util.Log;

import dk.redia.digiform.utils.ScanResultListener;


public class RFIDScannerThread extends Thread {
	
	private BluetoothSocket btSocket;
	private InputStream is;
	private OutputStream os;
	private boolean running;
	private String currentTagId;
	public static final String logTag = "RFIDScannerThread";
	
	private Set<ScanResultListener> listeners;
	
	public RFIDScannerThread(BluetoothSocket btSocket) throws IOException {
		setName("RFID Scanner");
		this.btSocket = btSocket;
		listeners = new HashSet<ScanResultListener>();
		if (btSocket==null) {
			throw new IOException("null BluetoothSocket instance provided to RFIDScannerThread");
		}
		os =  btSocket.getOutputStream();
		is = btSocket.getInputStream();
	}
	
	public void addListener(ScanResultListener listener) {
		listeners.add(listener);
	}
	
	public void removeListener(ScanResultListener listener) {
		listeners.remove(listener);
	}
	
	public void run() {
		running = true;
		// Apparently the RFID scanner takes a little while to get ready 
		// after a connection has been established, so we wait a little while
		// before spamming it with scan commands
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
 		while (running) {
			try {
				os.write("\r001400\r".getBytes());
				os.flush();
				try {
					Thread.sleep(250);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//notifyListenersLog(logTag, "Wrote SCAN command to RFID scanner");				
				String response = readResponse(is);
				if (response==null) {
					notifyListenersDisconnected();
					return;
				}
				if (response.length()<4) {
					// Remember to unset the current tag id
					currentTagId = null;
					continue;
				}
				// Remove initial 4 characters, which contain a response code
				// Remaining characters contain the tag id
				String tid = response.substring(4);
				notifyListenersLog(logTag, "Found tag with id " + tid);
				// Skip scan result if we found the same tag id during our last
				// scan
				if (currentTagId!=null && currentTagId.equals(tid)) {
					notifyListenersLog(logTag, "We already found this tag, so no need to announce it again");
					continue;
				}
				
				// Write command to retrieve payload data from the "Smart Label"
				String cmd = "\r402401" + tid + "0010\r"; 
				os.write(cmd.getBytes());
				os.flush();
				try {
					Thread.sleep(250);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				String data = readResponse(is);
				if (data.length()<4) {
					notifyListenersLog(logTag, "Failed to read data from RFID tag");
					continue;
				}
				// Strip first 2 characters, as they contain a response code
				
				data = data.substring(2);
				// Decode payload
				data = fromHex(data);
			//	notifyListenersLog(logTag,"scanner got data: "+ data);
				Pattern p = Pattern.compile("([0-9]*)[a-zA-Z]?DK.*");
				Matcher m = p.matcher(data);
				String resourceId = null;
				if (m.matches()) {
					resourceId = m.group(1);
				} else {
					notifyListenersLog(logTag, "Unable to parse RFID payload: " + data);
					continue;
				}
				
			//	notifyListenersLog(logTag, "resourceID: "+resourceId);
				// We are sure to have successfully read valid data from the tag
				// so make a note that we did, and avoid reading it again and again.
				currentTagId = tid;
				notifyListenersScanned(resourceId);
//				return fromHex(data);
				
			} catch (IOException e) {
				Log.d(logTag, "IOException during communication with scanner: " + e.getMessage());
				notifyListenersDisconnected();
				return;
			}
		}
	}
	
	private String fromHex(String str) {
		StringBuilder ascii = new StringBuilder();
		for (int i = 0; i < str.length(); i += 2) {
			String part = str.substring(i, i + 2);
			if (!part.equals("00")) {
				char c = (char)Integer.parseInt(part, 16);
				if ((c<='9' && c>='0') || (c>='a' && c<='z') || (c>='A' && c<='Z')) {
					ascii.append(c);
				} else {
					Log.d(logTag, "Skipping non-printable in hex parsing");
				}
			}
		}
		return ascii.toString();
	}
	
	private String readResponse(InputStream is) {
		byte previous = '\0';
		byte[] buffer = new byte[1];
		int bytesRead;
		String result = new String();
		//boolean responseRead = false;
		try {
			//int bytesRead = 0;
			//if(is.available() > 0){
			while ((bytesRead = is.read(buffer, 0, buffer.length))>=0) {
				//uiLog(logTag, "Read " + bytesRead + " bytes");
				if (buffer[0]=='\n' && previous=='\r') {
					return result;
				}
				if (buffer[0]!='\n' && buffer[0]!='\r') {
					result += (char)buffer[0];
				}
				previous = buffer[0];
			}
			//}
			return result;
			
		} catch (IOException e) {
			notifyListenersLog(logTag, "Error in readResponse: "+e.getMessage());
			e.printStackTrace();
			notifyListenersDisconnected();
			return null;
		}
		
	}
	
	private void notifyListenersDisconnected() {
		for (ScanResultListener listener : listeners) {
			listener.disconnected();
		}
	}
	
	private void notifyListenersScanned(String resourceId) {
		for (ScanResultListener listener : listeners) {
			listener.scanned(resourceId);
		}		
	}
	
	private void notifyListenersLog(String logTag, String msg) {
		for (ScanResultListener listener : listeners) {
			listener.log(msg);
		}	
	}
}
